package com.yzy.fly.config;

import cn.hutool.core.util.ArrayUtil;
import com.google.common.collect.Lists;
import com.yzy.fly.bean.FlyCustomBeanDefinitionRegistryPostProcessor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultSingletonBeanRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.lang.annotation.Annotation;
import java.util.*;

@Slf4j
@Component
public class SpringContextUtil implements BeanFactoryPostProcessor, ApplicationContextAware {
    private static ConfigurableListableBeanFactory beanFactory;
    public static ApplicationContext applicationContext;

    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        SpringContextUtil.beanFactory = beanFactory;
    }

    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        SpringContextUtil.applicationContext = applicationContext;
    }

    public static <T> T getBean(String name) {
        return applicationContext == null ? null : (T) applicationContext.getBean(name);
    }

    public static <T> T getBean(String name, Class<T> requiredType) {
        return applicationContext == null ? null : applicationContext.getBean(name, requiredType);
    }

    public static <T> T getBean(Class<T> requiredType) {
        return applicationContext == null ? null : applicationContext.getBean(requiredType);
    }

    public static String[] getBeanNamesForType(Class<?> type) {
        return applicationContext == null ? null : applicationContext.getBeanNamesForType(type);
    }

    public static <T> Map<String, T> getBeansOfType(Class<T> requiredType) {
//        Map<String, T> beans = applicationContext.getBeansOfType(requiredType);
        return applicationContext == null ? null : applicationContext.getBeansOfType(requiredType);
    }

    @Deprecated
    public static <T> T getBeansOfType(Class<T> requiredType, Class<?> requiredModelType) {
        Map<String, T> beans = getBeansOfType(requiredType);
        T t = null;
        for (Map.Entry<String, T> bean : beans.entrySet()) {
            T value = bean.getValue();
//            ParameterizedType thisType = (ParameterizedType) value.getClass().getGenericSuperclass();
//            Type type = thisType.getActualTypeArguments()[0];
//            Type type = ((ParameterizedType) (value.getClass()).getGenericInterfaces()[0]).getActualTypeArguments()[0];

//            Type<?> parametrizedClass=
//            if (type.isAssignableFrom(requiredModelType)) {
//            if (type.getClass() == (requiredModelType)) {
//                t = bean.getValue();
//            }
        }
        return t;
    }

    public static boolean containsBean(String name) {
        return applicationContext.containsBean(name);
    }

    public static boolean isSingleton(String name) {
        return applicationContext.isSingleton(name);
    }

    public static Class<? extends Object> getType(String name) {
        return applicationContext.getType(name);
    }

    public static ConfigurableListableBeanFactory getConfigurableBeanFactory() throws Exception {
        ConfigurableListableBeanFactory factory;
        if (null != beanFactory) {
            factory = beanFactory;
        } else {
            if (!(applicationContext instanceof ConfigurableApplicationContext)) {
                throw new Exception("No ConfigurableListableBeanFactory from context!");
            }
            factory = ((ConfigurableApplicationContext) applicationContext).getBeanFactory();
        }

        return factory;
    }

    /**
     * registerSingleton 会绕过AOP
     *
     * @param beanName
     * @param bean
     * @param <T>
     * @throws Exception
     */
    @Deprecated
    public static <T> void registerBean(String beanName, T bean) throws Exception {
        ConfigurableListableBeanFactory factory = getConfigurableBeanFactory();
        factory.autowireBean(bean);
        factory.registerSingleton(beanName, bean);
    }

    public static void registerBean(Class clazz) {
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
        registerBean(null, beanDefinitionBuilder.getRawBeanDefinition());
    }

    public static void registerBean(String beanName, Class clazz) {
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
        registerBean(beanName, beanDefinitionBuilder.getRawBeanDefinition());
    }

    private static void registerBean(String beanName, BeanDefinition beanDefinition) {
//        if (StringUtils.isBlank(beanName)) {
//            beanName = beanDefinition.getFactoryBeanName();
//        }
        ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) applicationContext;
        BeanDefinitionRegistry beanDefinitonRegistry = (BeanDefinitionRegistry) configurableApplicationContext
                .getBeanFactory();
        //获取BeanFactory
//        DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableApplicationContext.getAutowireCapableBeanFactory();
        beanDefinitonRegistry.registerBeanDefinition(beanName, beanDefinition);
//        defaultListableBeanFactory.registerBeanDefinition(beanName, beanDefinition);
        FlyCustomBeanDefinitionRegistryPostProcessor flyCustomBeanDefinitionRegistryPostProcessor = getBean(FlyCustomBeanDefinitionRegistryPostProcessor.class);
        if (flyCustomBeanDefinitionRegistryPostProcessor != null) {
            flyCustomBeanDefinitionRegistryPostProcessor.addBeanDefinitionInfo(beanName, beanDefinition);
        }
    }

    public static void unregisterBean(String beanName) throws Exception {
        ConfigurableListableBeanFactory factory = getConfigurableBeanFactory();
        if (factory instanceof DefaultSingletonBeanRegistry) {
            DefaultSingletonBeanRegistry registry = (DefaultSingletonBeanRegistry) factory;
            registry.destroySingleton(beanName);
        } else {
            throw new Exception("Can not unregister bean, the factory is not a DefaultSingletonBeanRegistry!");
        }
    }

    /**
     * 获取当前的环境配置，无配置返回null
     *
     * @return 当前的环境配置
     */
    public static String[] getActiveProfiles() {
        return applicationContext.getEnvironment().getActiveProfiles();
    }

    /**
     * 获取当前的环境配置，当有多个环境配置时，只获取第一个
     *
     * @return 当前的环境配置
     */
    public static String getActiveProfile() {
        final String[] activeProfiles = getActiveProfiles();
        return ArrayUtil.isNotEmpty(activeProfiles) ? activeProfiles[0] : null;
    }

    /**
     * 如果给定的bean名字在bean定义中有别名，则返回这些别名
     *
     * @param name
     * @return
     * @throws NoSuchBeanDefinitionException
     */
    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
        return beanFactory.getAliases(name);
    }

    /**
     * 获取 @Service等 的所有 bean 名称
     * @return /
     */
    public static List<String> getBeanNamesForAnnotation(Class<? extends Annotation> annotation) {
        return Lists.newArrayList(Arrays.asList(applicationContext.getBeanNamesForAnnotation(annotation)));
    }

    public static Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotation) {
        return applicationContext.getBeansWithAnnotation(annotation);
    }

    /**
     * 获取SpringBoot 配置信息
     *
     * @param property     属性key
     * @param defaultValue 默认值
     * @param requiredType 返回类型
     * @return /
     */
    public static <T> T getProperties(String property, T defaultValue, Class<T> requiredType) {
        T result = defaultValue;
        try {
//            result = getBean(Environment.class).getProperty(property, requiredType);
            result = applicationContext.getEnvironment().getProperty(property, requiredType);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 获取SpringBoot 配置信息
     *
     * @param property 属性key
     * @return /
     */
    public static String getProperties(String property) {
        return getProperties(property, null, String.class);
    }

    /**
     * 获取SpringBoot 配置信息
     *
     * @param property     属性key
     * @param requiredType 返回类型
     * @return /
     */
    public static <T> T getProperties(String property, Class<T> requiredType) {
        return getProperties(property, null, requiredType);
    }

}